from datetime import datetime
from typing import Optional, List

from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from fastapi_pagination import Page, Params
from starlette.responses import JSONResponse

from app.auth.dependencies import create_access_token, require_roles
from app.auth.models import User, UserWhole, Role, RoleWhole
from app.auth.services import auth_service
from app.core.models import Result

router = APIRouter()


@router.post("/token")
async def token(form_data: OAuth2PasswordRequestForm = Depends()):
    user = await auth_service.authc_user(form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token = create_access_token(form_data.username, user.id)
    response = JSONResponse(content={
        "access_token": access_token,
        "token_type": "bearer"
    })
    response.set_cookie(key="token", value=access_token)
    return response


@router.get("/users")
@require_roles("admin")
async def list_users(status: Optional[int] = None,
                     nickname: Optional[str] = None,
                     name: Optional[str] = None,
                     created_at_0: Optional[datetime] = None,
                     created_at_1: Optional[datetime] = None,
                     params: Params = Depends()
                     ) -> Result[Page[User]]:
    filters = []
    if status:
        filters.append(User.status == status)
    if nickname:
        filters.append(User.nickname.like(f"%{nickname}%"))
    if name:
        filters.append(User.name.like(f"%{name}%"))
    if created_at_0:
        filters.append(User.created_at >= created_at_0)
    if created_at_1:
        filters.append(User.created_at < created_at_0)

    data = await auth_service.list_users(params, *filters)
    return Result(data)


@router.get("/users/{user_id}")
async def detail_user(user_id: int) -> Result[UserWhole]:
    data = await auth_service.detail_user(user_id)
    return Result(data)


@router.post("/users")
@require_roles("admin")
async def create_user(user: UserWhole) -> Result[UserWhole]:
    user.id = None
    user = await auth_service.save_user(user)
    return Result(user)


@router.post("/users/{user_id}")
@require_roles("admin")
async def update_user(user_id: int, user: UserWhole) -> Result[UserWhole]:
    user.id = user_id
    user = await auth_service.save_user(user)
    return Result(user)


@router.get("/roles")
@require_roles("admin")
async def list_roles() -> Result[List[Role]]:
    data = await auth_service.get_all_roles()
    return Result(data)


@router.get("/roles/{role_id}")
@require_roles("admin")
async def detail_role(role_id: int) -> Result[RoleWhole]:
    data = await auth_service.detail_role(role_id)
    return Result(data)


@router.post("/roles")
@require_roles("admin")
async def create_role(role: Role) -> Result[Role]:
    role.id = None
    role = await auth_service.save_role(role)
    return Result(role)


@router.post("/roles/{role_id}")
@require_roles("admin")
async def update_role(role_id: int, role: Role) -> Result[Role]:
    role.id = role_id
    role = await auth_service.save_role(role)
    return Result(role)


@router.post("/users/{user_id}/roles")
@require_roles("admin")
async def grant_roles(user_id: int, role_ids: List[int]) -> Result[int]:
    n = await auth_service.grant_user_roles(user_id, role_ids)
    return Result(n)


@router.delete("/roles/{role_id}")
@require_roles("admin")
async def delete_role(role_id: int) -> Result:
    await auth_service.delete_role(role_id)
    return Result()